package com.pobing.zebra;

import android.util.Log;

import com.symbol.emdk.EMDKManager;
import com.symbol.emdk.EMDKManager.EMDKListener;
import com.symbol.emdk.EMDKResults;
import com.symbol.emdk.barcode.BarcodeManager;
import com.symbol.emdk.barcode.ScanDataCollection;
import com.symbol.emdk.barcode.ScanDataCollection.ScanData;
import com.symbol.emdk.barcode.Scanner;
import com.symbol.emdk.barcode.Scanner.DataListener;
import com.symbol.emdk.barcode.Scanner.StatusListener;
import com.symbol.emdk.barcode.Scanner.TriggerType;
import com.symbol.emdk.barcode.ScannerConfig;
import com.symbol.emdk.barcode.ScannerException;
import com.symbol.emdk.barcode.ScannerResults;
import com.symbol.emdk.barcode.StatusData;
import com.symbol.emdk.barcode.StatusData.ScannerStates;

import org.apache.cordova.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

public class ZebraScannerPlugin extends CordovaPlugin implements EMDKListener, StatusListener, DataListener {

    private static final String TAG = "ZebraScanner";

    // Variables to hold EMDK related objects
	private EMDKManager emdkManager = null;
	private BarcodeManager barcodeManager = null;
	private Scanner scanner = null;

    private CallbackContext currentCallbackContext = null;


    @Override
    public void onOpened(EMDKManager emdkManager) {
        // Get a reference to EMDKManager
        this.emdkManager =  emdkManager;

        // Get a  reference to the BarcodeManager feature object 
        initBarcodeManager();

        // Initialize the scanner
        initScanner();
    }

    @Override
    public void onClosed() {
            // The EMDK closed unexpectedly. Release all the resources.
        if (emdkManager != null) {
            emdkManager.release();
            emdkManager= null;
        }
        Log.d(TAG, "EMDK closed unexpectedly! Please close and restart the application.");
    }

    @Override
    public void onStatus(StatusData statusData) {
        // The status will be returned on multiple cases. Check the state and take the action.
        // Get the current state of scanner in background
        ScannerStates state =  statusData.getState();
        String statusStr = "";
        // Different states of Scanner
        switch (state) {
            case IDLE:
            // Scanner is idle and ready to change configuration and submit read.
            statusStr = statusData.getFriendlyName()+" is   enabled and idle...";
            // Change scanner configuration. This should be done while the scanner is in IDLE state.
            //setConfig();
            try {
                // Starts an asynchronous Scan. The method will NOT turn ON the scanner beam, 
                //but puts it in a  state in which the scanner can be turned on automatically or by pressing a hardware trigger.
                scanner.read();
            } catch (ScannerException e)   {
                Log.d(TAG,e.getMessage());
            }
            break;
        case WAITING:
            // Scanner is waiting for trigger press to scan...
            statusStr = "Scanner is waiting for trigger press...";
            break;
        case SCANNING:
            // Scanning is in progress...
            statusStr = "Scanning...";
            break;
        case DISABLED:
            // Scanner is disabled
            statusStr = statusData.getFriendlyName()+" is disabled.";
            break;
        case ERROR:
            // Error has occurred during scanning
            statusStr = "An error has occurred.";
            break;
        default:
            break;
        }

        // callback status to view
        Log.d(TAG,"Scanner Status ->" + statusStr);
    }


    @Override
    public void onData(ScanDataCollection scanDataCollection) {
        // The ScanDataCollection object gives scanning result and the collection of ScanData. Check the data and its status. 
        String dataStr = "";
        if (scanDataCollection != null && scanDataCollection.getResult() == ScannerResults.SUCCESS) {
            ArrayList<ScanData> scanData =  scanDataCollection.getScanData();
            // Iterate through scanned data and prepare the data. 
            for (ScanData data :  scanData) {
                // Get the scanned data
                String barcodeData =  data.getData();
                // Get the type of label being scanned
                ScanDataCollection.LabelType labelType = data.getLabelType();
                // Concatenate barcode data and label type
                dataStr =  barcodeData;
            }
            // callback data to view
            if (this.currentCallbackContext != null) {
                PluginResult result = new PluginResult(PluginResult.Status.OK,dataStr);
                result.setKeepCallback(true);
                this.currentCallbackContext.sendPluginResult(result);
            }
        }
        
    }

    @Override
    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
        super.initialize(cordova, webView);

		//The EMDKManager object will be created and returned in the callback.
        EMDKResults results = EMDKManager.getEMDKManager(this.cordova.getActivity().getApplicationContext(), this);
		
		// Check the return status of getEMDKManager() and update the status TextView accordingly.
		if (results.statusCode!=   EMDKResults.STATUS_CODE.SUCCESS) {
			Log.d(TAG,"EMDKManager object request failed!");
		} else {    
			Log.d(TAG,"EMDKManager object initialization is   in   progress.......");
		}

        // initBarcodeManager();
        // initScanner();
    }


	// Disable the scanner instance
	private void disableScanner() {
		if (scanner != null) {
			try {
				scanner.cancelRead();

				scanner.removeDataListener(this);
				scanner.removeStatusListener(this);
				scanner.disable();

			} catch (ScannerException e) {
				// TODO Auto-generated catch block
				Log.d(TAG, "Status: " + e.getMessage());
			}
			scanner = null;
		}

	}


	private void initBarcodeManager() {
		// Get the feature object such as BarcodeManager object for accessing the feature.
		barcodeManager =  (BarcodeManager)emdkManager.getInstance(EMDKManager.FEATURE_TYPE.BARCODE);
		// Add external scanner connection listener.
		if (barcodeManager == null) {
			Log.d(TAG, "Barcode scanning is not supported.");
		}
    }


	// Method to initialize and enable Scanner and its listeners
	private void initScanner() {
		if (scanner == null) {
            // Get default scanner defined on the device
            scanner = barcodeManager.getDevice(BarcodeManager.DeviceIdentifier.DEFAULT);
            if(scanner != null) {
                // Implement the DataListener interface and pass the pointer of this object to get the data callbacks.
                scanner.addDataListener(this);

                // Implement the StatusListener interface and pass the pointer of this object to get the status callbacks.
                scanner.addStatusListener(this);

                // Hard trigger. When this mode is set, the user has to manually            
                // press the trigger on the device after issuing the read call.            
                // NOTE: For devices without a hard trigger, use TriggerType.SOFT_ALWAYS.
                scanner.triggerType =  TriggerType.HARD;

                try{
                // Enable the scanner                
                // NOTE: After calling enable(), wait for IDLE status before calling other scanner APIs 
                // such as setConfig() or read().
                scanner.enable();

                } catch (ScannerException e) { 
                    Log.d(TAG,e.getMessage());
                    disableScanner();
                }
            } 
            else {
                    Log.d(TAG,"Failed to   initialize the scanner device.");
            }    
		}

	}


    @Override
    public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
        // This function take the name of the device to instantiate the scanner
       if ("claim".equals(action)) {
            // We claim the access to the device
            if (scanner != null) {
                try {
                    initScanner();

                    currentCallbackContext.success();
                } catch (Exception e) {
                    e.printStackTrace();
                    currentCallbackContext.error("Unable to claim reader");
                }
            } else {
                currentCallbackContext.error("Reader not open");
            }

            return true;
        } else if ("release".equals(action)) {
            // We release the barcodeReader if exist
            if (scanner != null) {
                disableScanner();
                callbackContext.success();
            } else {
                callbackContext.error("Reader not open");
            }

            return true;
        } else if ("register".equals(action)) {
            //We register the current callback context
            currentCallbackContext = callbackContext;

            PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
            result.setKeepCallback(true);
            callbackContext.sendPluginResult(result);

            return true;
        } else if ("unregister".equals(action)) {
            // We unregister the current callback context
            currentCallbackContext = null;

            return true;
        } else if ("enableTrigger".equals(action)) {
            // Enable the device trigger (true by default)
            Log.d(TAG, "enableTrigger");

            if (scanner == null) {
                currentCallbackContext.error("no scanner reader initalized");
            } else {
                try {
                    initScanner();
                    currentCallbackContext.success();
                } catch (Exception e) {
                    currentCallbackContext.error(e.getMessage());
                }
            }

            return true;
        } else if ("disableTrigger".equals(action)) {
            // Disable the device trigger
            Log.d(TAG, "disableTrigger");

            if (scanner == null) {
                currentCallbackContext.error("no scanner reader initalized");
            } else {
                try {
                    disableScanner();
                    currentCallbackContext.success();
                } catch (Exception e) {
                    currentCallbackContext.error(e.getMessage());
                }
            }

            return true;
        }  else if (action.equals("softwareTriggerStart")) {
            barcodeReaderChange(true);

            return true;
        } else if (action.equals("softwareTriggerStop")) {
            barcodeReaderChange(false);
            
            return true;

        } else if (action.equals("listen")) {
            this.currentCallbackContext = callbackContext;
             cordova.getThreadPool().execute(() -> {
                    PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
                    result.setKeepCallback(true);
                    callbackContext.sendPluginResult(result);
            });
            return true;
        }

        return false;
    }

    @Override
    public void onDestroy() {
        if (barcodeManager != null) {
            // close barcodeManager  to clean up resources.
            barcodeManager  = null;
        }
	 // Release all the EMDK resources
		if (emdkManager != null) {
			emdkManager.release();
			emdkManager= null;
		}

        super.onDestroy();
    }

    private void NotifyError(String error) {
        if (this.currentCallbackContext != null) {
            PluginResult result = new PluginResult(PluginResult.Status.ERROR, error);
            result.setKeepCallback(true);
            this.currentCallbackContext.sendPluginResult(result);
        }
    }

    private void barcodeReaderChange(boolean changeStatus){
        if (scanner != null) {
            try {
                if(changeStatus){
                    scanner.read();
                }else{
                    scanner.cancelRead();
                }
            } catch (ScannerException e) {
                Log.e(TAG, e.getMessage());
                NotifyError("ScannerException");
            }
        }
    }

}
